module GenericFunctions where

import Data.Maybe
import Data.Time.Format
import Data.Time.Calendar
import Data.Time.LocalTime
import Control.Exception
import Control.Monad
import System.IO
import System.Locale
import System.Directory
import System.FilePath

-- Function used to reset certain file content (creates file if it does not exist)
resetFile path = do System.Directory.createDirectoryIfMissing True (fst $ System.FilePath.splitFileName path)
                    hFile <- openFile path ReadWriteMode
                    hClose hFile

-- Function used to print out the message and then flush the stdout buffer
putStrFl str =  do  putStr str
                    hFlush stdout
                                        
putStrWF msg width =    putStrFl $ msg ++ replicate (width - (length msg)) ' '
                    
prompt what = prompt' what (\x -> True)

-- prompts for input with given message, repeats until input is valid
prompt' what f =    do  putStrFl $ what ++ ": "
                        x <- try readLn :: (Read t0) => IO (Either SomeException t0)
                        case x of
                            Left e  ->  invalid
                            Right v ->  if (f v) then return v
                                        else invalid
                    where invalid = putStrFl "Invalid data. " >> prompt' what f
                        
-- prompts for text line (with spaces)                        
promptLine what =   do  putStrFl $ what ++ ": "
                        x <- System.IO.getLine
                        return x

-- Function used to get date and validate it's correctness                        
promptDate what =   do  date <- promptLine $ what ++ " (yyyy-mm-dd)"
                        let parsed_m = parseTime defaultTimeLocale "%F" date :: Maybe Day
                        if (isJust parsed_m && date == (dateStr $ fromJust parsed_m)) then
                            return (fromJust parsed_m)
                        else
                            putStrFl "Invalid date. " >> promptDate what
                            
-- Function used to get time from the user and validate it's correctness
promptTime what =   do  time <- promptLine $ what ++ " (hh:mm)"
                        let parsed_m = parseTime defaultTimeLocale "%R" time :: Maybe LocalTime
                        if (isJust parsed_m && time == (timeStr $ fromJust parsed_m)) then
                            return (fromJust parsed_m)
                        else
                            putStrFl "Invalid time. " >> promptTime what
                            
-- Function used to print out time in a proper format 
timeStr time = formatTime defaultTimeLocale "%R" time

-- Function used to print out date in a proper format
dateStr time = formatTime defaultTimeLocale "%F" time
                                    
-- creates label using size characters and centering text
createLabel label size fill =   let fillsize = (size - (length label) - 2)
                                    halffillL = fillsize `div` 2
                                    halffillR = halffillL + (fillsize `mod` 2)
                                in
                                    (replicate halffillL fill) ++ " " ++ label ++ " " ++ (replicate halffillR fill)                           

-- Function used to show items without a header                        
showItems title itemsList = showItems' title itemsList False                                                    
                                
-- Function used to show items
showItems' title itemsList hasHeader = 
    do  putStrLn $ "\n" ++ createLabel title 120 '-'
        if (hasHeader) then do
            putStrFl $ " *) " ++ (head itemsList) ++ "\n"
            putStrFl $ menuText (tail itemsList) 1
        else
            putStrFl $ menuText itemsList 1
        putStrLn $ createLabel "-" 120 '-'
        where
            menuText [] inum = []
            menuText (i:is) inum = " " ++ (show inum) ++ ") " ++ i ++ "\n" ++ menuText is (inum + 1)                                  

-- Function used to print out a menu a getting user choice (in args content)
showMenu title actionList = do  showItems title [fst i | i <- actionList]
                                choice <- prompt' "Your choice" (\c -> c >= 1 && c <= length actionList)
                                snd $ actionList !! (choice - 1)                             

-- Function used to wait for a user's action
pressEnter = promptLine "Press ENTER to continue..." >> return ()


changeNull val1 val2 =  if (null val1) then val2
                        else val1
                        

